/********************************************************/
/*	Copyright (C) 2016 Gong Li Bin		 	*/
/*	Project:	GlbLib-1.0.0			*/
/*	Author:		gong_libin			*/
/*	Date:		2016_06_01			*/
/*	File:		GlbQueue.cpp			*/
/********************************************************/

#include "GlbQueue.h"

namespace GlbCls
{

CGlbQueue::CGlbQueue()
{
	m_ulTotal = 0;
	m_pHead = NULL;
	m_pTail = NULL;
	m_bMulti = true;
	if (GLB_SUCCESS != pthread_cond_init(&m_stCond, NULL)) throw(strerror(errno));
	if (GLB_SUCCESS != pthread_mutex_init(&m_stLock, NULL)) throw(strerror(errno));
}

CGlbQueue::CGlbQueue(bool bMulti)
{
	m_ulTotal = 0;
	m_pHead = NULL;
	m_pTail = NULL;
	m_bMulti = bMulti;
	if (true == bMulti) {
		if (GLB_SUCCESS != pthread_cond_init(&m_stCond, NULL)) throw(strerror(errno));
		if (GLB_SUCCESS != pthread_mutex_init(&m_stLock, NULL)) throw(strerror(errno));
	}
	else {
		memset(&m_stCond, '\0', sizeof(pthread_cond_t));
		memset(&m_stLock, '\0', sizeof(pthread_mutex_t));
	}
}

CGlbQueue::~CGlbQueue()
{
	void* pCur = NULL;

	m_ulTotal = 0;
	while (NULL != (pCur = m_pHead)) {
		m_pHead = *(void**)((UCHAR*)pCur + sizeof(UINT) + *(UINT*)pCur);
		free(pCur);
	}
	m_pTail = NULL;

	if (true == m_bMulti) {
		pthread_cond_destroy(&m_stCond);
		pthread_mutex_destroy(&m_stLock);
	}
}

void* CGlbQueue::GlbQueueHasNext(void* pCur)
{
	void* pReturn = NULL;

	if (true == m_bMulti) {
		GlbQueueLock();
		while (0 == m_ulTotal) {
			pthread_cond_wait(&m_stCond, &m_stLock);
		}
	}

	if (NULL != pCur) {
		pReturn = *(void**)((UCHAR*)pCur + sizeof(UINT) + *(UINT*)pCur);
	}
	else {
		pReturn = m_pHead;
	}

	if (true == m_bMulti) {
		GlbQueueUnlock();
	}

	return pReturn;
}

ULONG CGlbQueue::GlbQueueGetTotal()
{
	return m_ulTotal;
}

int CGlbQueue::GlbQueuePop(void* pData, UINT uiSize)
{
	void* pCur = NULL;
	int iReturn = GLB_SUCCESS;

	if (true == m_bMulti) {
		GlbQueueLock();
		while (0 == m_ulTotal) {
			pthread_cond_wait(&m_stCond, &m_stLock);
		}
	}

	pCur = m_pHead;
	if (NULL != pCur) {
		if (*(UINT*)pCur < uiSize) {
			(int)(m_ulTotal - 1) < 0 ? m_ulTotal = 0 : m_ulTotal -= 1;
			m_pHead = *(void**)((UCHAR*)pCur + sizeof(UINT) + *(UINT*)pCur);
			memcpy(pData, (UCHAR*)pCur + sizeof(UINT), *(UINT*)pCur);
			free(pCur);
		}
		else {
			iReturn = GLB_FAILURE;
		}
	}
	else {
		iReturn = GLB_FAILURE;
		m_ulTotal = 0;
	}

	if (true == m_bMulti) {
		GlbQueueUnlock();
	}

	return iReturn;
}

int CGlbQueue::GlbQueuePut(void* pData, UINT uiSize)
{
	void* pPut = NULL;
	int iReturn = GLB_SUCCESS;

	if (true == m_bMulti) {
		GlbQueueLock();
	}

	if (m_ulTotal > 0) {
		if (m_ulTotal < GLB_QUEUE) {
			if (NULL != m_pTail) {
				if (NULL != (pPut = GlbQueueNew(pData, uiSize))) {
					memcpy((UCHAR*)m_pTail + sizeof(UINT) + *(UINT*)m_pTail, &pPut, sizeof(void*));
					m_pTail = pPut;
					m_ulTotal += 1;
				}
			}
			else {
				GLB_ERROR("Total error.\n");
				iReturn = GLB_FAILURE;
			}
		}
		else {
			GLB_ERROR("Total full.\n");
			iReturn = GLB_FAILURE;
		}
	}
	else {
		if (NULL != (pPut = GlbQueueNew(pData, uiSize))) {
			m_pHead = m_pTail = pPut;
			m_ulTotal += 1;
		}
	}

	if (true == m_bMulti) {
		GlbQueueUnlock();
		pthread_cond_signal(&m_stCond);
	}

	return iReturn;
}

int CGlbQueue::GlbQueueGet(void* pData, UINT uiSize)
{
	void* pCur = NULL;
	void* pLeft = NULL;
	void* pRight = NULL;
	int iReturn = GLB_FAILURE;

	if (true == m_bMulti) {
		GlbQueueLock();
		while (0 == m_ulTotal) {
			pthread_cond_wait(&m_stCond, &m_stLock);
		}
	}

	pCur = pLeft = m_pHead;
	while (NULL != pCur) {
		pRight = *(void**)((UCHAR*)pCur + sizeof(UINT) + *(UINT*)pCur);
		if (*(UINT*)pCur == uiSize) {
			(int)(m_ulTotal - 1) < 0 ? m_ulTotal = 0 : m_ulTotal -= 1;
			memcpy(pData, (UCHAR*)pCur + sizeof(UINT), *(UINT*)pCur);
			if (m_pHead != pCur) {
				memcpy((UCHAR*)pLeft + sizeof(UINT) + *(UINT*)pLeft, pRight, sizeof(void*));
			}
			else {
				m_pHead = pRight;
			}
			iReturn = GLB_SUCCESS;
			free(pCur);
			break;
		}
		pLeft = pCur;
		pCur = pRight;
	}

	if (true == m_bMulti) {
		GlbQueueUnlock();
	}

	return iReturn;
}

void* CGlbQueue::GlbQueueNew(void* pData, UINT uiSize)
{
	void* pPut = NULL;

	if (NULL != (pPut = malloc(sizeof(UINT) + sizeof(UCHAR) * uiSize + sizeof(void*)))) {
		memcpy(pPut, &uiSize, sizeof(UINT));
		memcpy((UCHAR*)pPut + sizeof(UINT), pData, uiSize);
		memset((UCHAR*)pPut + sizeof(UINT) + uiSize, '\0', sizeof(void*));
	}
	else {
		GLB_ERROR("%s\n", strerror(errno));
	}

	return pPut;
}

} /* GlbCls */
